using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Terra.Core.Common.ApiResponse;
using Terra.Core.Common.Exceptions;
using Terra.Core.Common.ErrorCodes.Constants;
using Terra.Core.Common.ErrorCodes.Models;
using Terra.Core.Common.Enums.Logging;
using Terra.Data.Entities.Logging;
using Terra.Data.Repository.Logging;
using UAParser;
using Microsoft.Extensions.DependencyInjection;

namespace Terra.Service.Common.Infrastructure.ExceptionHandling;

/// <summary>
/// 全局异常处理中间件
/// </summary>
/// <remarks>
/// 该中间件用于统一处理应用程序中的所有未处理异常，确保返回统一的API响应格式。
/// 主要功能：
/// 1. 捕获所有未处理的异常
/// 2. 根据异常类型返回对应的错误响应
/// 3. 记录异常日志
/// 4. 设置响应的HTTP状态码
/// 5. 包含请求跟踪ID
/// </remarks>
public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;
    private readonly IServiceProvider _serviceProvider;
    private readonly Parser _uaParser;

    /// <summary>
    /// 初始化全局异常处理中间件
    /// </summary>
    /// <param name="next">请求处理委托</param>
    /// <param name="logger">日志记录器</param>
    /// <param name="serviceProvider">服务提供者</param>
    public GlobalExceptionMiddleware(
        RequestDelegate next,
        ILogger<GlobalExceptionMiddleware> logger,
        IServiceProvider serviceProvider)
    {
        _next = next;
        _logger = logger;
        _serviceProvider = serviceProvider;
        _uaParser = Parser.GetDefault();

        _logger.LogInformation("GlobalExceptionMiddleware已初始化");
    }

    /// <summary>
    /// 处理HTTP请求
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <returns>异步任务</returns>
    /// <remarks>
    /// 处理流程：
    /// 1. 尝试执行后续中间件
    /// 2. 捕获可能发生的异常
    /// 3. 调用异常处理方法
    /// </remarks>
    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            _logger.LogInformation("开始处理请求: {Path}", context.Request.Path);
            await _next(context);
            _logger.LogInformation("请求处理完成: {Path} {Message}", context.Request.Path, context.Response.StatusCode == 200 ? "成功" : "失败");
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "请求处理异常: Path={Path}, Message={Message}, StackTrace={StackTrace}",
                context.Request.Path,
                ex.Message,
                ex.StackTrace
            );
            await HandleExceptionAsync(context, ex);
        }
    }

    /// <summary>
    /// 处理捕获到的异常
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <param name="exception">捕获到的异常</param>
    /// <returns>异步任务</returns>
    /// <remarks>
    /// 异常处理流程：
    /// 1. 设置响应的Content-Type为application/json
    /// 2. 根据异常类型设置对应的HTTP状态码
    /// 3. 转换异常为API响应格式
    /// 4. 记录未处理的系统异常
    /// 5. 设置跟踪ID
    /// 6. 序列化响应并写入响应流
    /// </remarks>
    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        _logger.LogError(
            exception,
            "异常详情: Type={Type}, Message={Message}, StackTrace={StackTrace}",
            exception.GetType().FullName,
            exception.Message,
            exception.StackTrace
        );

        _logger.LogInformation("开始处理异常: {Type}", exception.GetType().Name);

        context.Response.ContentType = "application/json";
        ApiResponse response;
        ExceptionSeverity severity;

        switch (exception)
        {
            case ValidationException validationException:
                context.Response.StatusCode = (int)HttpStatusCode.UnprocessableEntity;
                response = ApiResponse.Error(
                    SystemErrorCodes.ValidationFailed,
                    validationException.Message,
                    ErrorSeverity.Error);
                severity = ExceptionSeverity.Warning;
                break;

            case BusinessException businessException:
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                response = ApiResponse.Error(
                    businessException.Code ?? BusinessErrorCodes.BusinessError,
                    businessException.Message,
                    ErrorSeverity.Error);
                severity = ExceptionSeverity.Warning;
                break;

            case PermissionException permissionException:
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                response = ApiResponse.Error(
                    permissionException.Code ?? SystemErrorCodes.Forbidden,
                    permissionException.Message,
                    ErrorSeverity.Error);
                severity = ExceptionSeverity.Warning;
                break;

            default:
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                response = ApiResponse.Error(
                    SystemErrorCodes.SystemError,
                    "服务器错误",
                    ErrorSeverity.Critical);
                severity = ExceptionSeverity.Error;
                break;
        }

        // 设置跟踪ID，用于问题排查
        response.TraceId = context.TraceIdentifier;

        _logger.LogInformation("准备记录异常日志");
        await LogExceptionAsync(context, exception, severity);
        _logger.LogInformation("异常日志记录完成");

        var result = JsonSerializer.Serialize(response, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });

        await context.Response.WriteAsync(result);
        _logger.LogInformation("异常处理完成");
    }

    /// <summary>
    /// 记录异常日志
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <param name="exception">异常信息</param>
    /// <param name="severity">异常严重程度</param>
    private async Task LogExceptionAsync(HttpContext context, Exception exception, ExceptionSeverity severity)
    {
        try
        {
            _logger.LogInformation("开始创建异常日志实体");

            var userAgent = context.Request.Headers.UserAgent.ToString();
            var clientInfo = !string.IsNullOrEmpty(userAgent)
                ? _uaParser.Parse(userAgent)
                : new ClientInfo("Unknown", new OS("Unknown", null, null, null, null),
                               new Device("Unknown", "Unknown", "Unknown"),
                               new UserAgent("Unknown", null, null, "Unknown"));

            var log = new ExceptionLog
            {
                Id = Guid.NewGuid(),
                ExceptionType = exception.GetType().FullName,
                ExceptionMessage = exception.Message,
                StackTrace = exception.StackTrace,
                Source = exception.Source,
                ExceptionTime = DateTime.UtcNow,
                TraceId = context.TraceIdentifier,
                RequestPath = context.Request.Path,
                RequestMethod = context.Request.Method,
                Level = severity == ExceptionSeverity.Error ? "Error" : "Warning",
                IpAddress = context.Connection.RemoteIpAddress?.ToString(),
                Browser = $"{clientInfo.UA.Family} {clientInfo.UA.Major}",
                OS = $"{clientInfo.OS.Family} {clientInfo.OS.Major}",
                Device = clientInfo.Device.Family,
                Severity = severity,
                CreatedTime = DateTime.UtcNow
            };

            _logger.LogInformation("准备获取异常日志仓储服务");
            using var scope = _serviceProvider.CreateScope();
            var exceptionLogRepository = scope.ServiceProvider.GetRequiredService<IExceptionLogRepository>();

            _logger.LogInformation("开始保存异常日志");
            await exceptionLogRepository.LogAsync(log);
            _logger.LogInformation("异常日志保存完成");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "记录异常日志时发生错误: {Message}", ex.Message);
        }
    }
}